In diesem Kapitel schauen wir uns an, wie JavaScript auf dem bestehenden DOM-Baum eines geladenen HTML-Dokuments angewendet werden kann. Das Document Object Model (DOM) ist eine W3C-Spezifikation einer Programmierschnittstelle, welche HTML- oder XML-Dokumente als eine Baumstruktur darstellt. In der Baumstruktur ist jeder Knoten ein Objekt, welches einen Teil des Dokumentes repräsentiert, wie ein Absatz, eine Überschrift, ein Video oder eine Tabellenzelle. Die Schnittstelle ist plattform- und programmiersprachenunabhängig und erlaubt damit standardisiert, die Struktur und das Layout eines Dokumentes zu verändern. In einem Internet-Browser bildet dies einen wichtigen Baustein für client-seitige dynamische Webseiten. Hier sehen wir eine HTML-Tabelle und deren Darstellung als Baum. Jedes HTML-Dokument kann in solch einer Baum-Darstellung abgebildet werden. Der Browser stellt bei DOM Level 0 ein Objekt-Modell zur Verfügung. Das Objekt-Modell enthält Collections von DOM-Knoten, auf die zugegriffen werden kann. DOM Level 0 ist keine offizielle DOM-Version. Sie wurde eingeführt von einigen Browser-Herstellern. Sie wurde nie formal spezifiziert. Sie ist inzwischen veraltet und sollte nicht mehr verwendet werden. DOM Level 2 ist die Schnittstelle zum Zugriff auf HTML-Dokumente. Sie ist formal spezifiziert und standardisiert durch das W3C. Sie wird von allen gängigen Browsern unterstützt. Sie kann auch von anderen Programmiersprachen als JavaScript verwendet werden. Es gibt folgende Erweiterungen gegenüber DOM Level 0: Alle Elemente und alle Attribute einer HTML-Seite sind voll ansprechbar. Ein Zugriff auf XHTML-Dokumente ist möglich. Die nachträgliche Erzeugung von Elementen und deren Einhängen in den Dokumenten-Baum wird möglich. Hier sehen wir die DOM Level 2 Struktur als UML-Klassendiagramm. Ausgangspunkt ist ein Knoten (Node). Ein HTML-Dokument ist ein Dokument und jedes Dokument ist ein Node. Ein HTML-Div-Element ist ein spezielles HTML-Element. Ein HTML-Element ist ein Element und jedes Element ist wiederum ein Node. Hier haben wir mal ein komplettes HTML-Dokument mit Head und Body. Im Body ist ein Absatz mit Inhalten sowie ein HTML-Formular. Das HTML-Formular beinhaltet zwei Textfelder sowie eine Auswahl zum Geschlecht. Dies ist die DOM Level 2 Baumstruktur des HTML-Dokumentes von der vorherigen Seite. Zu sehen sind die HTML-Elemente mit HTML als Wurzel sowie die textuellen Inhalte. Hier sind jetzt die verschiedenen Arten von Knoten mit verschiedenen Farben gekennzeichnet. Die HTML-Elementknoten sind orange. Die Textknoten sind rot. Und neu dazugekommen sind die Attribute von Elementen. Die Attribute sind blau. Außerdem sind Kommentare grün eingezeichnet. Auf diese Baumstruktur können Sie nun via JavaScript zugreifen. DOM Level 2 erweitert das document-Objekt um Methoden zum Erstellen von Knoten sowie um Methoden zum Auffinden von Knoten über ID, über das Name-Attribut und über den Elementnamen. Die JavaScript-Methoden lauten createElement, createAttribute, createTextNode sowie getElementById, getElementsByName und getElementsByTagName. Zusätzlich gibt es HTML5-Erweiterungen, die einen Zugriff über eine querySelector-Methode erlauben. Man kann sich entweder nur das erste Element zurückgeben lassen, auf das die Anfrage zutrifft oder alle Elemente, die man dann in JavaScript mit einer Schleife durchlaufen kann. In der Liste sehen Sie mögliche Selektoren sowie Beispiele dazu. Genaue Beschreibungen finden Sie in dem W3Schools-Link. So können Sie beispielsweise einzelne CSS-Klassen abfragen, ein einzelnes Element wie einen Absatz, Klassen von bestimmten Elementen wie „p.intro“, Listen von Elementen oder Pseudoklassen. Node, document und element haben Methoden zum Durchwandern und Manipulieren des Baums, insbesondere zur Erzeugung neuer Knoten mit Hilfe des document-Objektes, zum Durchwandern des Baums mit Hilfe der node-Objekte sowie Methoden zur Strukturänderung der node-Objekte. Der Zugriff auf DOM-Knoten kann direkt auf ein Element per eindeutiger ID erfolgen. Dazu muss jedes benötigte HTML-Element ein eindeutiges id-Attribut haben. Der Zugriff erfolgt dann via „document.getElementById“. Ebenso ist ein Zugriff über einen Element-Typ möglich, hier im Beispiel über „heading-3“. Dafür verwendet man die Methode „getElementsByTagName“. Die Rückgabe ist dann ein Array von diesen Elementen, die sich in dem HTML-Dokument befinden. Außerdem kann der Zugriff auf Elemente über deren name-Attribut erfolgen. Aber nicht jeder Tag darf ein name-Attribut haben und eventuell gibt es mehrere Elemente mit demselben Namen, beispielsweise bei Radiobuttons. Dabei wird die Methode „getElementsByName“ verwendet. Die Verwendung von alten DOM Level 0 Pfaden sollte man heutzutage vermeiden. Schauen wir uns noch weitere Möglichkeiten zum Zugriff auf den DOM-Baum an. Speziell beim Aufruf von Handlerfunktionen verweist „this“ auf das Element, in dem der Code steht. Mit dem onClick-Attribut eines HTML-Elements kann man eine JavaScript-Funktion aufrufen. Dies ist dann nur für einen Mausklick auf dieses HTML-Tag gültig. Dieses onClick-Attribut sollte aber heutzutage nicht mehr verwendet werden; stattdessen sollte ein JavaScript-Eventhandler hinzugefügt werden. Wie das geht, schauen wir uns später an. Eine weitere, veraltete Methode für manche Objekte ist der Zugriff ohne Nennung der Collection über das name-Attribut. Dies geschieht über das Dokument-Objekt. Dieser Zugriff sollte jedoch vermieden werden. Stattdessen sollten Sie ab jetzt „getElementsByName“ verwenden. Für die Adressierung ist generell die ID zu bevorzugen! Hier sind die Gründe dafür: Name ist nur bei manchen Tags zulässig. Name ist nicht eindeutig. Wenn mehrere gleichartige Elemente angesprochen werden sollen, kann man „class“ verwenden. Name hat unterschiedliche Bedeutungen, beispielsweise beim Anker als Sprungmarke oder beim Input-Element als Parametername. Name ist nur noch aus Kompatibilitätsgründen zulässig. Name ist im DOM 2 Core nicht enthalten. Die ID ist hingegen bei allen Tags zulässig! Knoten des DOM-Baumes werden durch node-Objekte repräsentiert. Mögliche Arten von Knoten sind Elementknoten, Textknoten oder Attributknoten. Hier sehen Sie Attribute des Node-Objektes. Da ist der Name, der Typ als Zahlenwert, der Wert des Knotens und der Rumpftext bei Textknoten. Außerdem können Sie ein Array aller Attribute des Knotens auslesen. Die Verbindung zu anderen Knoten können Sie über den Vaterknoten, über ein Array aller Kindknoten, über den ersten oder letzten Kindknoten sowie über den linken oder rechten Nachbarknoten ermitteln. Die gesamte DOM-Struktur ist beliebig manipulierbar. Sie können einen Knoten klonen, bei Bedarf auch rekursiv. Sie können fragen, ob Kindknoten vorhanden sind oder nicht. Sie können einen neuen Kindknoten hinten oder vorne anhängen oder entfernen. Sie können Attribute auslesen, setzen oder entfernen. Sie können Attributknoten erfragen, neu hinzufügen oder löschen. Letztlich lässt sich auch noch der Text eines Knotens beeinflussen. Es kann neuer Text an bestehendem Text angehängt werden, zwischendrin eingefügt, ersetzt und gelöscht werden. In den nächsten Abschnitten schauen wir uns die Beziehungen von Knoten untereinander einmal genauer an. Child-Nodes von Knoten zehn sind die Knoten elf, zwölf und dreizehn. Der Vaterknoten (parent) von Knoten zehn ist die eins. First Child von Knoten zehn ist Knoten elf. Last Child von Knoten zehn ist Knoten dreizehn. Previous Sibling von Knoten neun ist Knoten zwei. Following Sibling von Knoten neun ist Knoten zehn. Schauen wir nun, wie wir Zugriff auf die Attribute von HTML-Elementen bekommen können. Die Attribute sind in Unterknoten gespeichert. Der Zugriff erfolgt über „element.getAttribute“ beziehungsweise „element.setAttribute“. Hier sehen Sie, wie die Bilddatei eines bestehenden Image-Elements gesetzt werden kann. Alternativ dazu kann der Zugriff über die HTML-Erweiterungen von DOM 2 erfolgen. Die Objekte haben Attribute, die direkt angesprochen werden können. Die Namen entsprechen HTML-Namen. Als Ausnahme wird „class“ zu „className“. Hier sehen Sie, wie dieselbe Zuweisung auf die Bilddatei über die HTML-Erweiterungen erfolgen kann. Hier können Sie den DOM-Stil und den HTML-Stil direkt vergleichen. Zunächst wird im DOM-Stil ein leeres div-Element und ein neues Image-Element erzeugt. Das Image-Element wird dann dem Div-Element zugewiesen. Dann wird ein Absatz und Text erzeugt und der Text wird dann dem Absatz zugewiesen. Der Absatz wird abschließend auch dem div-Element zugeordnet. Auf diese Weise kann HTML dynamisch zur Laufzeit von JavaScript erzeugt und direkt client-seitig dargestellt werden. Genau dasselbe wird nun im HTML-Stil ausgeführt. Was man nun bevorzugt, ist Geschmackssache. Generell ist ziemlich viel Code nötig, um ein paar Elemente zu erzeugen. Dies kann bei komplexeren Anforderungen schnell unübersichtlich werden. Es gibt daher weit verbreitete Hilfsbibliotheken wie JQuery, die diese Codeerstellung syntaktisch vereinfachen. An dieser Stelle sollen aber nur die grundlegenden Möglichkeiten von JavaScript vorgestellt werden. Über den DOM-Baum haben Sie auch Zugriff auf CSS Inline-Styles. Sie können natürlich nicht auf eine externe CSS-Datei zugreifen, aber das CSS-Style-Attribut ist ja ein gewöhnliches HTML-Attribut. Die Inline-Styles haben gemäß der Kaskadierungsregeln Vorrang vor einer CSS-Datei. Die Werte sind Strings und enthalten Pixel, Prozent, Punkt oder Emphasis. Ein solcher Style ist als Unterobjekt realisiert, in diesem Beispiel erhält ein Element mit Namen „Hugo“ eine Schriftgröße von zwölf Punkt. Wie werden die CSS-Attributnamen gebildet? Bindestriche sind nicht zulässig in JavaScript-Bezeichnern. Also wird der Bindestrich entfernt und es wird der nächste Buchstabe groß geschrieben. So ergibt sich: „fontSize“, „fontWeight“ und „backgroundColor“. Text in einem Tag wird in Unterknoten gespeichert. Der Text steht im Attribut „nodeValue“. Er kann durch Zuweisung geändert werden. Hier sehen Sie den HTML-Ausschnitt und die graphische Baumdarstellung dazu. Wie sieht der JavaScript-Code dazu aus? Hier sehen Sie ein Beispiel für eine DOM-Manipulation. Alle Absätze im Dokument sollen nachträglich gelb eingefärbt werden. Dazu werden zunächst alle Absatz-Knoten in einem Array gespeichert und dieses dann mit einer for-Schleife durchlaufen. Dort wird dann die Hintergrundfarbe geändert. Das nachfolgende Prinzip sollte strikt vermieden werden, da es sehr schnell komplex wird. Sie können nämlich als inneres HTML eines Elementes einen beliebigen String hinzufügen. Dieser String kann natürlich prinzipiell beliebigen HTML-Code oder CSS-Code, ja sogar JavaScript-Code enthalten! Wesentlich besser ist es hingegen, einen neuen DOM-Knoten anzulegen. Dazu wird zuerst ein neuer leerer DOM-Knoten vom gegebenen Typ erzeugt. Dann wird ein Textknoten mit dem angegebenen Text als Inhalt erzeugt. Hier wird im Beispiel eine heading-zwei Überschrift erzeugt, die grün sein soll. Sie wird dann der Box mit der ID „box-eins“ zugewiesen. Jeder DOM-Knoten hat eine Reihe von Methoden, um den Baum zu verändern. So können Sie einen Knoten am Ende oder am Anfang der Kindknoten einfügen. Sie können einen Kindknoten entfernen oder durch einen anderen Knoten ersetzen. In diesem Beispiel sehen Sie abschließend, wie ein neuer Absatz in ein Element mit der ID „my\_id“ hinzugefügt wird. Danach werden alle Listenelemente in ein Array geschrieben. Dieses Array wird dann in einer for-Schleife durchlaufen. Danach werden einzelne Listenelemente bei Bedarf (siehe „if“-Verzweigung) entfernt.